package com.yanfan.modbus.frame.abs;


import com.yanfan.modbus.ModbusUtils;
import com.yanfan.modbus.code.ExceptionCode;
import com.yanfan.modbus.code.FunctionCode;
import com.yanfan.modbus.exception.IllegalFunctionException;
import com.yanfan.modbus.exception.ModbusTransportException;
import com.yanfan.modbus.frame.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;


abstract public class ModbusResponse extends ModbusFrame {

    protected byte exceptionCode = -1;

    public ModbusResponse(int slaveId) throws ModbusTransportException {
        super(slaveId);
    }

    public static ModbusResponse createModbusResponse(ByteBuf buf) throws ModbusTransportException {
        int slaveId = buf.readUnsignedByte();
        short functionCode = buf.readUnsignedByte();
        boolean isException = false;

        if (ModbusUtils.isErrorCode(functionCode)) {
            isException = true;
            functionCode -= ModbusUtils.MAX_FUNCTION_CODE;
        }

        ModbusResponse response = null;
        if (functionCode == FunctionCode.READ_COILS) {
            response = new ReadCoilsResponse(slaveId);
        } else if (functionCode == FunctionCode.READ_DISCRETE_INPUTS) {
            response = new ReadDiscreteInputsResponse(slaveId);
        } else if (functionCode == FunctionCode.READ_HOLDING_REGISTERS) {
            response = new ReadHoldingRegistersResponse(slaveId);
        } else if (functionCode == FunctionCode.READ_INPUT_REGISTERS) {
            response = new ReadInputRegistersResponse(slaveId);
        } else if (functionCode == FunctionCode.WRITE_COIL) {
            response = new WriteCoilResponse(slaveId);
        } else if (functionCode == FunctionCode.WRITE_REGISTER) {
            response = new WriteRegisterResponse(slaveId);
        } else if (functionCode == FunctionCode.READ_EXCEPTION_STATUS) {
            response = new ReadExceptionStatusResponse(slaveId);
        } else if (functionCode == FunctionCode.WRITE_COILS) {
            response = new WriteCoilsResponse(slaveId);
        } else if (functionCode == FunctionCode.WRITE_REGISTERS) {
            response = new WriteRegistersResponse(slaveId);
        } else if (functionCode == FunctionCode.REPORT_SLAVE_ID) {
            response = new ReportSlaveIdResponse(slaveId);
        } else if (functionCode == FunctionCode.WRITE_MASK_REGISTER) {
            response = new WriteMaskRegisterResponse(slaveId);
        } else {
            throw new IllegalFunctionException(functionCode, slaveId);
        }
        response.read(buf, isException);
        return response;
    }

    public static void main(String[] args) throws Exception {
        ByteBuf queue = Unpooled.buffer().writeBytes(new byte[]{3, 2});
        ModbusResponse r = createModbusResponse(queue);
        System.out.println(r);
    }

    public boolean isException() {
        return exceptionCode != -1;
    }

    public void setException(byte exceptionCode) {
        this.exceptionCode = exceptionCode;
    }

    void read(ByteBuf buf, boolean isException) {
        if (isException) {
            exceptionCode = buf.readByte();
        } else {
            readResponse(buf);
        }
    }

    abstract protected void readResponse(ByteBuf buf);

    @Override
    final protected void writeImpl(ByteBuf buf) {
        if (isException()) {
            buf.writeByte((byte) (getFunctionCode() + ModbusUtils.MAX_FUNCTION_CODE));
            buf.writeByte(exceptionCode);
        } else {
            buf.writeByte(getFunctionCode());
            writeResponse(buf);
        }
    }

    abstract protected void writeResponse(ByteBuf buf);

    public String getExceptionMessage() {
        return ExceptionCode.getExceptionMessage(exceptionCode);
    }

    public byte getExceptionCode() {
        return exceptionCode;
    }
}
